package chuckNorris;
//
//	File:			Brain.java
//	Author:		Krzysztof Langner
//	Date:			1997/04/28
//
//    Modified by:	Paul Marlow

//    Modified by:      Edgar Acosta
//    Date:             March 4, 2008



import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.Math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.*;

import classifier.RoboClassifier;

import rcscene.Action;
import rcscene.ObjectWeights;
import sceneInfo.FuzzyFramework;
import sceneInfo.FuzzySceneLib;
import sceneInfo.Scene;
import sceneInfo.SceneLib;
import sceneInfo.VisualInfo;

class Brain extends Thread implements SensorInput
{
	
	
	
	/**---------------------------------------------------------------------------
	// This constructor:
	// - stores connection to ChuckNorris
	// - reads configuration parameters from provided file
	// - creates the RoboClassifier with configuration parameters
	// - starts thread for this object
	 * 
	 * @param chuck
	 * @param team
	 * @param side
	 * @param number
	 * @param playMode
	 * @param configFile
	 * @param evaluationMode : set to true if the brain is just constructed
	 * @throws Exception 
	 * @throws NumberFormatException 
	 */
	public Brain(SendCommand chuck, String team, char side, int number, String playMode, String configFile) throws Exception
	{
		m_timeOver = false;
		this.chuck = chuck ;
		m_memory = new Memory();
		m_team = team;
		m_side = side;
		// m_number = number;
		m_playMode = playMode;
		
		////read configuration from file

		BufferedReader in = new BufferedReader(new FileReader(configFile));
		String str;
		StringTokenizer m_tokenizer;
		
		///////////////////////////////////////

		String inFile= "";
		boolean FuzzType = true; // indicates if we use fuzzy logic or not
		boolean useLog = false; // indicates if we read the trainingset from a log file
		boolean useArff = false;// indicates if we should use an arff file (ignored if useLog==true)
		List<Float> angleParameters = new ArrayList<Float>(); // angle parameters : with the List type we can create the list without knowing ahead of time how many we will have
		List<Float> distanceParameters = new ArrayList<Float>(); // distance paramerters
		ObjectWeights objWeights = new ObjectWeights();
		boolean objw_set = false; // if we set the weights
		String actionClassifierName = "weka.classifiers.trees.J48"; // default action classifier
		String regressionClassifierName = "weka.classifiers.trees.M5P"; // default regression classifier

		//READING FROM CONFIG FILE/////////////
		while ((str = in.readLine()) != null) //reading lines from config file to get parameter list
		{
			str.trim();
			if (str.startsWith("#")) continue;//comment in config file

			//tokenize line.
			m_tokenizer = new StringTokenizer(str);

			/////////////////////////////////////////////
			// get parameters from line in config file:
			/////////////////////////////////////////////

			//first token is parameter name :
			String paramname = m_tokenizer.nextToken();

			/*valid parameter names are :
			 * LogFile // next token is file name to read logs (if provided, arffFile is ignored)
			 * ArffFile // next token is file name to read arff format trainingset 
			 * AngleParameters // list of angle discretization parameters
			 * DistanceParameters // list of distance discretization parameters
			 * Fuzzy // boolean indicating whether the discretization uses fuzzy logic (use 0 or 1)
			 * ObjectWeights // weights to apply to object types for learning : for now weights accepted are only 0 or 1. 
			 *                 the order of the weights is : ball, teammate, opponent, unknown player, goal, flag, line
			 * ActionClassifier //type of classifier for determining the action (classification task) (full class name with package)
			 * RegressionClassifier //type of classifier to use for regression (determining parameter values)
			 */


			if (paramname.startsWith("LogFile")) // we are given a raw log file
			{
				useLog= true;
				inFile = m_tokenizer.nextToken();

			} else if (paramname.startsWith("ArffFile")) {
				if(!useLog){
					inFile = m_tokenizer.nextToken();
					useArff = true;
				}

			} else if (paramname.startsWith("AngleParam")) {
				//read the angle parameters : there should be nothing else on the line
				while (m_tokenizer.hasMoreTokens()){
					try {
						angleParameters.add(Float.parseFloat(m_tokenizer.nextToken()));
					}
					catch (Exception e){
						System.err.println("reading dist parameters: Exception caught!! " + e.toString());	
					}
				}
			} else if (paramname.startsWith("DistanceParam")) {
				while (m_tokenizer.hasMoreTokens()){
					try {
						distanceParameters.add(Float.parseFloat(m_tokenizer.nextToken()));
					}
					catch (Exception e){
						System.err.println("reading dist parameters: Exception caught!! " + e.toString());	
					}
				}
			} else if (paramname.startsWith("ObjectWeights")) {

				//read weights :ball, teammate, opponent, unknown player, goal, flag, line
				try {
					System.out.println("Setting Object Weights");
					objWeights.setBallWeight(Float.parseFloat(m_tokenizer.nextToken()));
					System.out.println("Ball"+objWeights.getBallWeight());
					objWeights.setTeamPlayerWeight(Float.parseFloat(m_tokenizer.nextToken()));
					System.out.println("Team"+objWeights.getTeamPlayerWeight());
					objWeights.setOpponentPlayerWeight(Float.parseFloat(m_tokenizer.nextToken()));
					System.out.println("Opponents"+objWeights.getOpponentPlayerWeight());
					objWeights.setUnknownPlayerWeight(Float.parseFloat(m_tokenizer.nextToken()));
					objWeights.setGoalWeight(Float.parseFloat(m_tokenizer.nextToken()));
					objWeights.setFlagWeight(Float.parseFloat(m_tokenizer.nextToken()));
					objWeights.setLinesWeight(Float.parseFloat(m_tokenizer.nextToken()));
					objw_set = true; // we've set the weights
				}
				catch (Exception e){
					System.err.println("Error Reading Object Weights !! " + e.toString());	
				}
			} else if (paramname.startsWith("Fuzzy")) {
				int Fuzz = Integer.parseInt(m_tokenizer.nextToken());
				if (!((Fuzz== 1)||(Fuzz==0))) // only 0 or 1 at this point
				{
					System.err.println("Error: invalid 'fuzzyness' parameter ("+FuzzType+ "), must be 0 or 1. Reverting to default= 0");
					FuzzType = true;
				}

			} else if (paramname.startsWith("ActionClassifier")) {
				actionClassifierName = m_tokenizer.nextToken();
				System.out.println("Action classifier set to :"+actionClassifierName);

			} else if (paramname.startsWith("RegressionClassifier")) {
				regressionClassifierName = m_tokenizer.nextToken();
				System.out.println("Regression classifier set to :"+regressionClassifierName);
			} else {
				System.err.println("Error: invalid parameter name"+paramname);
			}
		} // end while loop reading parameters
		
		// check if we have all the information we need :
		//either a log file or an arff file must be provided
		if(!(useLog || useArff)){
			throw new Exception("Error : no training data provided");
		}
				
		//we have default discretization parameters.
		float [] arrayAngleParam;
		float [] arrayDistanceParam;
		if (angleParameters.size()==0||distanceParameters.size()==0){
			System.out.println("Discretization parameters missing, using default values");
			arrayAngleParam = new float [] {-88.0f, -27.0f, -9.0f, 0.0f,3.0f, 13.0f, 29.0f, 88.0f};
			arrayDistanceParam = new float [] {1.1f, 4.1f, 22.2f, 48.4f,73.0f};
		} else {
			arrayAngleParam = new float[angleParameters.size()];
			for (int i = 0; i<angleParameters.size();i++){
				arrayAngleParam[i]=angleParameters.get(i).floatValue(); //get Float and convert to float
			}
			Arrays.sort(arrayAngleParam); //sort the parameters
			
			arrayDistanceParam = new float[distanceParameters.size()];
			for (int i = 0; i<distanceParameters.size();i++){
				arrayDistanceParam[i]=distanceParameters.get(i).floatValue(); //get Float and convert to float
			}
			Arrays.sort(arrayDistanceParam); //sort the parameters
			 
		}

		// we need object weights : if not provided we use default of 1 for all object types
		if(!objw_set){ 
			System.out.println("no object weights provided, using 1 all around");
			objWeights.setBallWeight(1.0f);
			objWeights.setTeamPlayerWeight(1.0f);
			objWeights.setOpponentPlayerWeight(1.0f);
			objWeights.setUnknownPlayerWeight(1.0f);
			objWeights.setGoalWeight(1.0f);
			objWeights.setFlagWeight(1.0f);
			objWeights.setLinesWeight(1.0f);
		}
		
		
		FuzzyFramework ff= new FuzzyFramework(arrayAngleParam,arrayDistanceParam);;
		
		if (useLog) {
			FuzzySceneLib sceneLib = new FuzzySceneLib(inFile);
			sceneLib.setFramework(ff);
			sceneLib.setObjectWeights(objWeights);
			sceneLib.discretizeThis(ff);
			chuckClassifier = new RoboClassifier(sceneLib, false); // false refers to evaluation or play mode
		} else //using arff
			chuckClassifier = new RoboClassifier(ff, new File(inFile), false,actionClassifierName ,regressionClassifierName ,objWeights); // get reference to input File object from arff file name 'inFile'
		
		//System.out.println(" lib has scenes :" +first.getSceneList().size());
		
		start();
	}


	//---------------------------------------------------------------------------
	// This is main brain function used to make decision
	// In each cycle we decide which command to issue based on

	 public void run()
	    {
		System.out.println(" are we about to play:"+m_playMode);

		// first put it somewhere on my side
		if(Pattern.matches("^before_kick_off.*",m_playMode))
			chuck.move( -Math.random()*52.5 , 34 - Math.random()*68.0 );

		while( !m_timeOver )
		{
			VisualInfo vi = m_memory.getvInfo();

			if (vi==null){
				//System.out.println(" I see nothing !");
				m_memory.waitForNewInfo();
			}
			else{
				Scene myscene = new Scene();
				myscene.setVision(vi);
				
				//System.out.println("I see : "+ vi.toString());

				Action myaction = chuckClassifier.classify(myscene);
				
				//System.out.println("I want to:"+myaction.toString());

				switch(myaction.getAction()){
				case Action.ACTION_KICK:
					chuck.kick(myaction.getActionPower(), myaction.getActionDirection());
					break;
				case Action.ACTION_DASH:
					//chuck.dash(69);
					chuck.dash(myaction.getActionPower());
					break;
				case Action.ACTION_TURN:
					chuck.turn(myaction.getActionDirection());
					
					/*if (myaction.getActionDirection() == 0){
						chuck.turn(30);
					}else{
						chuck.turn(myaction.getActionDirection());
					}*/
					break;
				default:
					//do nothing.

				}
			}
			// sleep one step to ensure that we will not send
			// two commands in one cycle.
			try{
				Thread.sleep(2*SoccerParams.simulator_step);
			}catch(Exception e){}
		}
		chuck.bye();
	    }


	    
	    //===========================================================================
	    // Implementation of SensorInput Interface

	    //---------------------------------------------------------------------------
	    // This function sends see information
	    public void see(VisualInfo info)
	    {
	    	//System.out.print("SEE!");
	    	m_memory.store(info);
	    	//System.out.println(info.toString());
	    }


	//---------------------------------------------------------------------------
	// This function receives hear information from player
	public void hear(int time, int direction, String message)
	{
	}

	//---------------------------------------------------------------------------
	// This function receives hear information from referee
	public void hear(int time, String message)
	{						 
		if(message.compareTo("time_over") == 0)
			m_timeOver = true;

	}


	//===========================================================================
	// Private members
	private SendCommand	                chuck;			// robot which is controled by this brain
	private Memory			m_memory;				// place where all information is stored
	private char			m_side;
	volatile private boolean		m_timeOver;
	private String                      m_playMode;
	private String m_team;
	private String m_ObjWeights;
	private RoboClassifier chuckClassifier;
	

}
